﻿namespace NetFx.SMIL.Definitions
{
  using System;
  using System.Text;
  using System.Collections;
  using System.Collections.Generic;

  using System.Reflection;
  using System.Reflection.Emit;
  using System.Runtime.InteropServices;

  using System.Diagnostics;
  using System.Diagnostics.CodeAnalysis;
  using System.Diagnostics.SymbolStore;
  using System.Security;
  using System.Security.Permissions;

  using NetFx.MSIL;
  using NetFx.SMIL.References;

  public class EnumDef : MemberDef
  {
    #region Statics
    public static EnumDef of(string name)
    {
      return new EnumDef(name);
    }
    public static EnumDef of(Type enumType)
    {
      return new EnumDef(enumType);
    }
    #endregion
    #region Fields
    protected TypeAttributes attributes;
    protected TypeRef underlingType;
    protected List<FieldDef> constants = null;
    #endregion
    #region Properties
    public virtual TypeAttributes Attributes
    {
      get
      {
        return this.attributes;
      }
      set
      {
        this.attributes = value;
      }
    }
    public override DefType DefinitionType
    {
      get { return DefType.Enum; }
    }
    public virtual TypeRef UnderlingType
    {
      get
      {
        return this.underlingType;
      }
      set
      {
        this.underlingType = value;
      }
    }
    public virtual List<FieldDef> Constants
    {
      get
      {
        return this.constants == null ?
          this.constants = new List<FieldDef>() :
          this.constants;
      }
    }
    #endregion
    #region Constructors
    public EnumDef(string name)
      : base(name)
    {

    }
    public EnumDef(Type enumType)
     : base(String.Empty)
    {
      this.OnReflecting(enumType);
    }
    public EnumDef(string name, Type underlingType)
      : base(name)
    {
      this.UnderlingType = TypeRef.of(underlingType);
    }
    #endregion
    #region Methods
    #region Publics
    public virtual FieldDef DefineConstant(string name)
    {
      return this.AddConstant(FieldDef.of(name));
    }
    public virtual FieldDef FindConstant(string name)
    {
      if (!string.IsNullOrEmpty(name) && this.constants != null)
      {
        foreach (FieldDef field in this.constants)
        {
          if (field != null && field.Name == name)
          {
            return field;
          }
        }
      }
      return null;
    }
    public virtual FieldDef RemoveConstant(string name)
    {
      return this.RemoveConstant(this.FindConstant(name));
    }
    #endregion
    #region Helpers
    protected virtual FieldDef AddConstant(FieldDef field)
    {
      if (field != null)
      {
        field.Parent = this;
        this.Constants.Add(field);
      }
      return field;
    }
    protected virtual FieldDef RemoveConstant(FieldDef field)
    {
      if (field != null)
      {
        field.Parent = null;
        this.Constants.Remove(field);
      }
      return field;
    }

    protected override AnyGenerator OnCompleting(AnyGenerator generator)
    {
      if (generator is EnumGenerator)
      {
        //TODO: Add generation!
      }
      return base.OnCompleting(generator);
    }
    protected override AnyGenerator OnDefining(AnyGenerator container)
    {
      if (container is ModuleGenerator)
      {
        return ((ModuleGenerator)container).DefineEnumeration(this.Name, this.Attributes, this.UnderlingType.ReflectInfo);
      }
      else if (container is TypeGenerator)
      {
        return ((TypeGenerator)container).DefineEnumeration(this.Name, this.Attributes, this.UnderlingType.ReflectInfo);
      }
      return base.OnDefining(container);
    }

    protected override ReflectDef OnReflecting(object source)
    {
      if (source is Type && (source as Type).IsEnum)
      {
        //TODO: Get the underlingType!
      }
      return base.OnReflecting(source);
    }
    protected override void OnBreakingReference()
    {
      this.Break(this.UnderlingType);

      base.OnBreakingReference();
    }
    #endregion
    #endregion
  }
}
